#pragma once
#ifndef _KH_CORE_IO_H
#define _KH_CORE_IO_H
#include <Kharlia/Core/Common.h>
#include <Kharlia/Core/ObjectBase.h>

namespace kharlia { namespace core {

void _InitIOBindings();

enum EIORequestState {
    IORS_FAILED,
    IORS_READY,
    IORS_EXECUTING,
    IORS_COMPLETED
};

enum EIORequestCallbackType {
    IOCT_NONE,
    IOCT_PYTHON,
    IOCT_CPP
};

// Error types
const uint8 IOERR_NONE = 0;
const uint8 IOERR_UNKNOWN = 1;
const uint8 IOERR_CALLBACK = 2;
// FileIO error types
const uint8 IOERR_OPENFAIL = 10;
const uint8 IOERR_EOF = 11;

// IO Request abstract base
class KH_CORE_DECLSPEC FIORequest: public FRefCounted, public boost::noncopyable {
protected:
    EIORequestState mState;
    uint8 mErrorCode;
    bool mDispatched;

public:
    FIORequest():
      mState(IORS_READY),
      mErrorCode(IOERR_NONE),
      mDispatched(false) {}
    virtual ~FIORequest() {}

    virtual void Execute() = 0;
    void ExecuteStackless();
    void ExecuteAsync(boost::function0<void> cb);

    KH_DEF_GET(EIORequestState, State)
    KH_DEF_GET(int, ErrorCode)
    KH_DEF_GET(bool, Dispatched)

    friend class CIOManager;
};

// Used by CIOManager to store information about a waiting request
struct FWaitInfo {
    // Channel used to wake the tasklet, only valid for synchronous wait
    handle<PyChannelObject> Channel;
    // The thread the request was issued from
    boost::thread::id ThreadID;
    // The request to be executed
    intrusive_ptr<FIORequest> Request;
    // Callback that is valid only for asynchronous requests
    boost::function0<void> Callback;
};

// Manages worker threads and waiting tasklets for stackless IO
class KH_CORE_DECLSPEC CIOManager: public CObject {
    boost::threadpool::pool mWorkerPool;
    std::list<shared_ptr<FWaitInfo>> mWaitingQueue;
    boost::mutex mWaitingMutex;
    bool mDispatching;

    static CIOManager* msInstance;

    KH_DECL_GC()

public:
    CIOManager(PyObject* self, uint8 poolsize=3);
    virtual ~CIOManager();

    void Dispatch(bool async);
    void Execute(intrusive_ptr<FIORequest> req);
    void ExecuteStackless(intrusive_ptr<FIORequest> req);
    void ExecuteAsync(intrusive_ptr<FIORequest> req, boost::function0<void> cb);
    str ReadFile(str name, int start, int length);
    str LoadFile(str name);
    void DumpFile(str name, str data);

    static void AsyncTaskletBase(object arg);
    static void AddPendingDispatchCall(bool allowexc=false);
    static CIOManager* GetInstancePtr();
    static shared_ptr<CIOManager> GetInstance();

    friend void _InitIOBindings();
};

inline CIOManager* CIOManager::GetInstancePtr() {
    return msInstance;
}

inline shared_ptr<CIOManager> CIOManager::GetInstance() {
    return msInstance? msInstance->GetSelfPtr<CIOManager>(): shared_ptr<CIOManager>();
}

KH_CORE_DECLSPEC tuple Stat_ReportDispatch();

// Derived request types, each type is responsible for executing a specific IO
// action

class KH_CORE_DECLSPEC FFileIORequest: public FIORequest {
public:
    FFileIORequest() {}
    virtual ~FFileIORequest() {}

    friend class CIOManager;
};

class KH_CORE_DECLSPEC FFileLoadRequest: public FFileIORequest {
    std::string mFileName;
    int mReadStart;
    int mReadLength;
    std::string mResult;

public:
    FFileLoadRequest(const std::string& InFileName, int InReadStart, int InReadLength):
      mFileName(InFileName), mReadStart(InReadStart), mReadLength(InReadLength) {}
    virtual ~FFileLoadRequest() {}

    virtual void Execute();

    KH_DEF_GET(const std::string&, Result)
};

class KH_CORE_DECLSPEC FFileDumpRequest: public FFileIORequest {
    std::string mFileName;
    std::string mDump;
    int mResult;

public:
    FFileDumpRequest(const std::string& InFileName, const std::string& InDump):
      mFileName(InFileName), mDump(InDump), mResult(0) {}
    virtual ~FFileDumpRequest() {}

    virtual void Execute();

    KH_DEF_GET(int, Result)
};

inline void FIORequest::ExecuteStackless() {
    assert(CIOManager::GetInstancePtr());
    CIOManager::GetInstancePtr()->ExecuteStackless(intrusive_ptr<FIORequest>(this));
}

inline void FIORequest::ExecuteAsync(boost::function0<void> cb) {
    assert(CIOManager::GetInstancePtr());
    CIOManager::GetInstancePtr()->ExecuteAsync(intrusive_ptr<FIORequest>(this), cb);
}

}} // namespace Kharlia

KH_HAS_BACK_REFERENCE(kharlia::core::CIOManager)

#endif